package cn.openea.perpartner.meily.web.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.openea.perpartner.common.core.constant.DateConstant;
import cn.openea.perpartner.common.core.exception.ServiceException;
import cn.openea.perpartner.common.core.utils.BeanUtils;
import cn.openea.perpartner.common.core.utils.DateUtils;
import cn.openea.perpartner.common.core.utils.StringUtils;
import cn.openea.perpartner.meily.thirdparty.UserSystemApi;
import cn.openea.perpartner.meily.web.entity.*;
import cn.openea.perpartner.meily.web.entity.vo.RevenueRecordsSearchVo;
import cn.openea.perpartner.meily.web.mapper.RevenueRecordsInsMapper;
import cn.openea.perpartner.meily.web.service.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service("revenueRecordsInsService")
public class RevenueRecordsInsServiceImpl extends ServiceImpl<RevenueRecordsInsMapper, RevenueRecordsIns> implements IRevenueRecordsInsService {

    @Autowired
    private IGroupBuyingService groupBuyingService;
    @Autowired
    private IGroupBuyingSubService groupBuyingSubService;
    @Autowired
    private IGoodsService goodsService;
    @Autowired
    private IRevenueRecordsArcService revenueRecordsArcService;
    @Resource
    private UserSystemApi userSystemApi;

    @Override
    public List<RevenueRecordsIns> listBySearchVo(RevenueRecordsSearchVo searchVo) {
        if (searchVo.getIds() != null && !searchVo.getIds().isEmpty()) {
            return baseMapper.selectBatchIds(searchVo.getIds());
        }
        QueryWrapper<RevenueRecordsIns> wrapper = new QueryWrapper<>();
        wrapper.eq("DATE_FORMAT(settlement_date,'%Y-%m')", searchVo.getTimePeriod().format(DateTimeFormatter.ofPattern(DateConstant.DEFAULT_MONTH_PATTERN)));

        List<RevenueRecordsIns> list = baseMapper.selectList(wrapper);
        // 判断是否关注公众号
        Map<String, String> map = userSystemApi.isBindOfficialAccount(list.stream()
                .map(RevenueRecordsIns::getUserId)
                .collect(Collectors.toList()));
        if (map == null || map.isEmpty()) {
            return list;
        }
        list.forEach(item -> {
            item.setBindOfficialAccount(StringUtils.isNotBlank(map.get(item.getUserId())) ? map.get(item.getUserId()) : "否");
        });
        return list;
    }

    @Override
    public BigDecimal countCumulative(LocalDate timePeriod) {
        if (timePeriod == null) {
            return BigDecimal.ZERO;
        }
        return baseMapper.countCumulative(timePeriod.format(DateTimeFormatter.ofPattern(DateConstant.DEFAULT_MONTH_PATTERN)));
    }

    /**
     * 获取两个月前的收益记录
     *
     * @return
     */
    @Override
    public List<RevenueRecordsIns> getLastMonthRecordsList() {
        return baseMapper.getLastMonthRecordsList();
    }


    /**
     * 统计区域服务商累计收益
     * 每天01:00分执行一次(因为是凌晨1点执行的，相当于是次日执行，需要判断是否跨月)
     * 步骤：
     * 一、统计当月的累计收益
     * 1.获取当前月已结束的拼团记录(meily_group_buying);
     * 2.遍历拼团记录，生成 拼团map
     * 3.遍历 拼团map，统计区域服务商累计收益
     * 4.遍历区域服务商map对象，更新收益记录
     * <p>
     * 二、每月月底自动归档区域服务商收益记录
     *
     * @return
     */
    @Scheduled(cron = "${revenue.auto_tatistical_income.cron}")
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public boolean autoTatisticalIncome() {

        System.out.println("***************************************");
        System.out.println("开始执行【统计区域服务商当月的累计收益】的定时任务............");

        // 当月或上个月已结束的拼团记录
        List<GroupBuying> groupFinishList = null;

        // 是否为当月的第一天
        boolean firstDayOfMonth = DateUtils.isFirstDayOfMonth();

        /**
         * 第1步 获取当月或上个月已结束的拼团记录
         */
        if (firstDayOfMonth) {
            // 是当月的第一天，表示已到了下个月，则需要统计上个月的数据
            groupFinishList = groupBuyingService.getLastMonthFinishList();
        } else {
            // 不是当月的第一天，获取当前月已结束的拼团记录
            groupFinishList = groupBuyingService.getCurrentMonthFinishList();
        }

        if (groupFinishList == null) {
            System.out.println("没有已结束的拼团，无需计算累计收益，执行结束！(执行日期：" + DateUtil.now() + ")");
            return true;
        }

        /**
         * 第2步 遍历拼团记录，生成 拼团map
         */
        // 存储 每个拼团记录 的 对应的区域服务商奖励、商品总销量、订单量、商品量
        Map<String, Object> groupMap = new HashMap<String, Object>();
        for (GroupBuying groupBuying : groupFinishList) {

            // 2.1 根据 拼团id 获取 所有购买记录
            List<GroupBuyingSub> allbuyinglist = groupBuyingSubService.getAllJoinList(groupBuying.getId());
            if (allbuyinglist == null) {
                continue;
            }

            // 2.2 根据 商品id 获取 商品详情
            Goods goods = goodsService.getById(groupBuying.getGoodsId());
            if (goods != null) {
                // 2.3 计算商品总销量
                long actualSales = 0;// 实际销量
                for (GroupBuyingSub groupBuyingSub : allbuyinglist) {
                    actualSales += Long.parseLong(groupBuyingSub.getBuyQuantity());
                }
                long customSales = 0;// 商品自定义销量
                if (StrUtil.isNotBlank(goods.getCustomSales())) {
                    customSales = Long.parseLong(goods.getCustomSales());
                }

                // 商品总销量 = 商品实际销量 + 商品自定义销量
                long totalSales = actualSales + customSales;

                String providerCashback = "0";// 区域服务商返现金额
                String groupType = "";// 团购类型。百人团/千人团/万人团
                if (totalSales > 100 && totalSales <= 1000) {
                    providerCashback = goods.getHundredProviderCashback();
                    groupType = "百人团";
                } else if (totalSales > 1000 && totalSales <= 10000) {
                    providerCashback = goods.getThousandProviderCashback();
                    groupType = "千人团";
                } else if (totalSales > 10000) {
                    providerCashback = goods.getTenThousandProviderCashback();
                    groupType = "万人团";
                }

                JSONObject object = new JSONObject();
                object.put("orderQuantity", allbuyinglist.size());// 订单量
                object.put("goodsQuantity", actualSales);// 商品量
                object.put("providerCashback", providerCashback);// 区域服务商返现金额
                object.put("groupType", groupType);// 团购类型。百人团/千人团/万人团
                groupMap.put(groupBuying.getId(), object);
            }

        }

        /**
         * 第3步 遍历 拼团map，统计区域服务商累计收益
         */
        // 存储每个区域服务商的订单数量、商品销量、累计收益
        Map<String, Object> providerMap = new HashMap<String, Object>();

        // 前月或上个月已结束的拼团的所有参与记录（按参与时间降序）
        List<GroupBuyingSub> joinList = null;

        // 3.1 获取当前月已结束的拼团的所有参与记录（不含当天）
        if (firstDayOfMonth) {
            // 是当月的第一天，表示已到了下个月，则需要统计上个月的数据
            joinList = groupBuyingSubService.getLastMonthGroupFinishJoinList();
        } else {
            // 不是当月的第一天，获取当前月已结束的拼团记录
            joinList = groupBuyingSubService.getCurrentMonthGroupFinishJoinList();
        }

        if (joinList != null && joinList.size() > 0) {
            // 3.2 遍历参与记录，统计区域服务商累计收益
            for (GroupBuyingSub groupBuyingSub : joinList) {

                // 记录最新一条参与记录的参与时间
                String lastJoinTime = "";
                if (StrUtil.isBlank(lastJoinTime)) {
                    if (groupBuyingSub.getJoinTime() != null) {
                        lastJoinTime = groupBuyingSub.getJoinTime();
                    }
                }

                // 若区域服务商为空，则跳过记录
                if (StrUtil.isBlank(groupBuyingSub.getSupplierId())) {
                    continue;
                }

                // 获取区域商信息
                JSONObject supplierObject = JSONObject.parseObject(groupBuyingSub.getSupplier());

                // 若区域服务商为空，则跳过
                if (supplierObject == null || supplierObject.isEmpty()) {
                    continue;
                }

                // 若区域服务商编号为空，则跳过
                if (StrUtil.isBlank(supplierObject.getString("providerId"))) {
                    continue;
                }

                // 若区域服务商是玫丽平台的，则跳过
                if ("00000001".equals(supplierObject.getString("providerId"))) {
                    continue;
                }

                // 根据 拼团id 获取 拼团map
                JSONObject groupObj = (JSONObject) groupMap.get(groupBuyingSub.getMainDocunid());
                if (groupObj == null) {
                    continue;
                }

                // 判断key是否存在
                if (providerMap.containsKey(groupBuyingSub.getSupplierId())) {

                    // 存在，则重新计算object
                    JSONObject object = (JSONObject) providerMap.get(groupBuyingSub.getSupplierId());

                    // 累计收益
                    double _accumulatedIncome = Double.parseDouble(object.getString("accumulatedIncome")) + (Double.parseDouble(groupObj.getString("providerCashback")) * Long.parseLong(groupBuyingSub.getBuyQuantity()));

                    object.put("userId", groupBuyingSub.getSupplierId());// 区域服务商userid
                    object.put("nickName", groupBuyingSub.getSupplierName());// 区域服务商昵称
                    object.put("providerId", supplierObject.getString("providerId"));// 区域服务商编号
                    object.put("area", supplierObject.getString("area"));// 所属地区
                    object.put("street", supplierObject.getString("street"));// 所属街道
                    object.put("address", supplierObject.getString("address"));// 地址
                    object.put("storeName", supplierObject.getString("storeName"));// 店铺名称
                    object.put("phone", supplierObject.getString("phone"));// 手机号码
                    object.put("orderQuantity", object.getLong("orderQuantity") + 1);// 订单数量 + 1
                    object.put("goodsQuantity", object.getLong("goodsQuantity") + Long.parseLong(groupBuyingSub.getBuyQuantity()));// 商品销量
                    object.put("accumulatedIncome", String.format("%.2f", _accumulatedIncome));// 累计收益
                    object.put("bindOfficialAccount", "否");// 绑定公众号（是/否）
                    object.put("status", "未结算");// 区域服务商编号
                    object.put("lastJoinTime", lastJoinTime);// 最新的参与时间
                    providerMap.put(groupBuyingSub.getSupplierId(), object);

                } else {

                    // 累计收益
                    double _accumulatedIncome = Double.parseDouble(groupObj.getString("providerCashback")) * Long.parseLong(groupBuyingSub.getBuyQuantity());

                    JSONObject object = new JSONObject();
                    object.put("userId", groupBuyingSub.getSupplierId());// 区域服务商userid
                    object.put("nickName", groupBuyingSub.getSupplierName());// 区域服务商昵称
                    object.put("providerId", supplierObject.getString("providerId"));// 区域服务商编号
                    object.put("area", supplierObject.getString("area"));// 所属地区
                    object.put("street", supplierObject.getString("street"));// 所属街道
                    object.put("address", supplierObject.getString("address"));// 地址
                    object.put("storeName", supplierObject.getString("storeName"));// 店铺名称
                    object.put("phone", supplierObject.getString("phone"));// 手机号码
                    object.put("orderQuantity", 1);// 订单数量 + 1
                    object.put("goodsQuantity", 0);// 商品销量
                    object.put("accumulatedIncome", String.format("%.2f", _accumulatedIncome));// 累计收益
                    object.put("bindOfficialAccount", "否");// 绑定公众号（是/否）
                    object.put("status", "未结算");// 区域服务商编号
                    object.put("lastJoinTime", lastJoinTime);// 最新的参与时间
                    providerMap.put(groupBuyingSub.getSupplierId(), object);
                }
            }
        }

        /**
         * 第4步 遍历区域服务商map对象，更新收益记录
         */
        for (Map.Entry<String, Object> entry : providerMap.entrySet()) {

            String userId = entry.getKey();
            JSONObject object = JSON.parseObject(JSON.toJSONString(entry.getValue()), JSONObject.class);

            // 4.1 获取当月的区域服务商收益记录是否存在
            LambdaQueryWrapper<RevenueRecordsIns> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(RevenueRecordsIns::getUserId, userId);

            if (firstDayOfMonth) {
                // 是当月的第一天，表示已到了下个月，则需要统计上个月的数据
                wrapper.last(" and DATE_FORMAT(create_time, '%Y%m') = DATE_FORMAT(date_sub(CURDATE(), interval 1 month), '%Y%m') ");
            } else {
                // 不是当月的第一天，获取当前月的记录
                wrapper.last(" and DATE_FORMAT(create_time, '%Y%m') = DATE_FORMAT(CURDATE(), '%Y%m') ");
            }

            RevenueRecordsIns revenueRecordsIns = getOne(wrapper);

            if (revenueRecordsIns == null || BeanUtil.isEmpty(revenueRecordsIns)) {
                revenueRecordsIns = new RevenueRecordsIns();
            }

            // 4.2 重新赋值，更新数据
            revenueRecordsIns.setUserId(object.getString("userId"));
            revenueRecordsIns.setNickName(object.getString("nickName"));
            revenueRecordsIns.setProviderId(object.getString("providerId"));
            revenueRecordsIns.setArea(object.getString("area"));
            revenueRecordsIns.setStreet(object.getString("street"));
            revenueRecordsIns.setAddress(object.getString("address"));
            revenueRecordsIns.setStoreName(object.getString("storeName"));
            revenueRecordsIns.setPhone(object.getString("phone"));
            revenueRecordsIns.setOrderQuantity(object.getString("orderQuantity"));
            revenueRecordsIns.setGoodsQuantity(object.getString("goodsQuantity"));
            revenueRecordsIns.setAccumulatedIncome(object.getString("accumulatedIncome"));
            revenueRecordsIns.setBindOfficialAccount(object.getString("bindOfficialAccount"));
            revenueRecordsIns.setStatus(object.getString("status"));
            revenueRecordsIns.setUpdateTime(DateUtil.now());
            revenueRecordsIns.setSettlementDate(DateUtil.today() + " 23:59:59");
            revenueRecordsIns.setLastJoinTime(object.getString("lastJoinTime"));

            if (!saveOrUpdate(revenueRecordsIns)) {
                log.error("新增或更新区域服务商的收益记录出错了！");
                log.error("执行中断。");
                System.out.println("***************************************");
                throw new ServiceException("新增或更新区域服务商的收益记录出错了!");
            }
        }

        if (firstDayOfMonth) {
            // 是当月的第一天，表示已到了下个月，则需要自动归档区域服务商收益记录
            if (!autoArchiveRevenueRecords()) {
                throw new ServiceException("当月最后一天，自动归档区域服务商收益记录出错了!");
            }
        }

        System.out.println("执行成功！");
        System.out.println("***************************************");
        return true;
    }

    /**
     * 自动归档区域服务商收益记录（每月最后一天执行）
     * 步骤：
     * 1.获取一个月前的收益记录(meily_revenue_records_ins);
     * 2.将记录写入到收益记录归档表中(meily_revenue_records_arc);
     * 3.将两个月前的记录从当前表(meily_revenue_records_ins)删除
     *
     * @return
     */
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public boolean autoArchiveRevenueRecords() {

        System.out.println("***************************************");
        System.out.println("开始执行【自动归档区域服务商收益记录】的定时任务............");

        // 再次判断，是否为某月最后一天，若不是，则不执行定时任务
        // 获取当前时间的前一天
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.DATE, -1);
        if (!(calendar.get(Calendar.DATE) == calendar.getActualMaximum(Calendar.DATE))) {
            System.out.println("不是当月最后一天 无需执行定时任务!");
            System.out.println("执行结束!");
            System.out.println("***************************************");
            return true;
        }

        /**
         * 第1步 获取上个月的收益记录(meily_revenue_records_ins);
         */
        List<RevenueRecordsIns> lastMonthRecordsList = getLastMonthRecordsList();

        /**
         * 第2步 将上个月的收益记录写入到收益记录归档表中(meily_revenue_records_arc);
         */
        for (RevenueRecordsIns revenueRecordsIns : lastMonthRecordsList) {

            RevenueRecordsArc rrArc = new RevenueRecordsArc();

            // 赋值
            BeanUtils.copyBeanProp(rrArc, revenueRecordsIns);
            String yesterday = DateUtil.format(DateUtil.yesterday(), "yyyy-MM-dd 23:59:59");
            rrArc.setSettlementDate(yesterday);// // 修改结算日期为某月最后一天的23:59:59

            // 写入归档表
            if (!revenueRecordsArcService.add(rrArc)) {
                log.error("将收益记录添加到归档表出错了，出错记录：" + revenueRecordsIns.getId());
                log.error("执行中断。");
                System.out.println("***************************************");

                throw new ServiceException("将收益记录添加到归档表出错了，出错记录：" + revenueRecordsIns.getId());
            }
        }

        /**
         * 第3步 将两个月前的收益记录从当前表(meily_revenue_records_ins)删除
         */
        LambdaQueryWrapper<RevenueRecordsIns> wrapper = new LambdaQueryWrapper<>();
        wrapper.last(" where DATE_FORMAT(create_time, '%Y%m') = DATE_FORMAT(date_sub(CURDATE(), interval 3 month), '%Y%m')");
        if (baseMapper.delete(wrapper) < 0) {
            log.error("将两个月前的从当前表(meily_revenue_records_ins)删除出错了!");
            log.error("执行中断。");
            System.out.println("***************************************");

            throw new ServiceException("将两个月前的从当前表(meily_revenue_records_ins)删除出错了!");
        }

        /*List<String> ids = twoMonthsAgoRecordsList.stream().map(RevenueRecordsIns::getId).collect(Collectors.toList());
        if(!removeByIds(ids)){
            log.error("将记录从当前表(meily_revenue_records_ins)删除出错了!");
            log.error("执行中断。");
            System.out.println("***************************************");
            return false;
        }*/

        System.out.println("执行成功!");
        System.out.println("***************************************");
        return true;
    }

}